﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Xml;
using System.IO;

namespace TwitterAPI
{
    public class Client
    {
        private Connection connection;

        private static List<FeedItem> GetTwitsByHashCodeImplementation(string url)
        {
            List<FeedItem> ret = new List<FeedItem>();
            WebRequest request = HttpWebRequest.Create(url);
            using (WebResponse response = request.GetResponse())
            {
                Client.FindResponseErrors(response);
                using (XmlReader reader = XmlReader.Create(response.GetResponseStream()))
                {
                    if (reader.ReadToDescendant("entry"))
                    {
                        do
                        {
                            FeedItem message = ParseFeedItem(reader);
                            ret.Add(message);
                        } while (reader.ReadToFollowing("entry"));
                    }
                }
            }
            return ret;
        }

        public static List<FeedItem> GetTwitsByHashCode(string hashCode)
        {
            return GetTwitsByHashCodeImplementation(string.Format(@"http://search.twitter.com/search.atom?tag={0}", Uri.EscapeDataString(hashCode)));
        }

        public static List<FeedItem> GetTwitsByHashCode(string hashCode, DateTime since)
        {
            return GetTwitsByHashCodeImplementation(string.Format(@"http://search.twitter.com/search.atom?tag={0}&since={1}", Uri.EscapeDataString(hashCode), Uri.EscapeUriString(since.ToString(("yyyy-MM-dd")))));
        }

        public static List<FeedItem> GetTwitsByHashCode(string hashCode, long since)
        {
            return GetTwitsByHashCodeImplementation(string.Format(@"http://search.twitter.com/search.atom?tag={0}&since_id={1}", Uri.EscapeDataString(hashCode), since));
        }

        private List<FeedItem> GetRepliesImplementation(string url)
        {
            List<FeedItem> ret = new List<FeedItem>();
            WebRequest request = connection.CreateRequest(url);
            using (WebResponse response = request.GetResponse())
            {
                FindResponseErrors(response);
                using (XmlReader reader = XmlReader.Create(response.GetResponseStream()))
                {
                    if (reader.ReadToDescendant("status"))
                    {
                        do
                        {
                            FeedItem message = ParseReply(reader);
                            ret.Add(message);
                        } while (reader.ReadToFollowing("status"));
                    }
                }
            }
            return ret;
        }

        public List<FeedItem> GetReplies(int since)
        {
            return GetRepliesImplementation(string.Format(@"http://www.twitter.com/replies.xml?since_id={0}", since));
        }

        public List<FeedItem> GetReplies(DateTime since)
        {
            return GetRepliesImplementation(string.Format(@"http://www.twitter.com/replies.xml?since={0}", Uri.EscapeUriString(since.ToString(("ddd, dd MMM yyyy HH':'mm':'ss 'GMT'")))));
        }

        public List<FeedItem> GetReplies()
        {
            return GetRepliesImplementation(@"http://www.twitter.com/replies.xml");
        }

        internal static void FindResponseErrors(WebResponse response)
        {
            if (response.ResponseUri.AbsoluteUri == "http://twitter.com/400.html")
            {
                throw new TwitterException("Too many requests for the period of time");
            }
            if (response.ResponseUri.AbsoluteUri == "http://twitter.com/401.html")
            {
                throw new TwitterException("Invalid Credentials");
            }
            if (response.ResponseUri.AbsoluteUri == "http://twitter.com/403.html")
            {
                throw new TwitterException("Invalid request");
            }
            if (response.ResponseUri.AbsoluteUri == "http://twitter.com/404.html")
            {
                throw new TwitterException("Information not found");
            }
            if (response.ResponseUri.AbsoluteUri == "http://twitter.com/500.html")
            {
                throw new TwitterException("Internal server error");
            }
            if (response.ResponseUri.AbsoluteUri == "http://twitter.com/502.html")
            {
                throw new TwitterException("Twitter is down at the moment");
            }
            if (response.ResponseUri.AbsoluteUri == "http://twitter.com/503.html")
            {
                throw new TwitterException("Server too busy");
            }
        }

        private static FeedItem ParseReply(XmlReader reader)
        {
            long id = reader.ReadToFollowing("id") ? Int64.Parse(reader.ReadElementContentAsString()) : 0;
            string text = reader.ReadToFollowing("text") ? reader.ReadElementContentAsString() : "";
            reader.ReadToFollowing("user");
            string user = reader.ReadToDescendant("name") ? reader.ReadElementContentAsString() : "";
            string imageUrl = reader.ReadToFollowing("profile_image_url") ? reader.ReadElementContentAsString() : "";
            //TODO: Use User class instead

            FeedItem message = new FeedItem(user, text, imageUrl, id);
            return message;
        }
        
        private static FeedItem ParseFeedItem(XmlReader reader)
        {
            long id = 0;
            if (reader.ReadToFollowing("id"))
            {
                string sid = reader.ReadElementContentAsString();
                Int64.TryParse(sid.Substring(sid.IndexOf(':', 4) + 1), out id);
            }
            string text = reader.ReadToFollowing("title") ? reader.ReadElementContentAsString() : "";
            string imageUrl = reader.ReadToFollowing("link") && reader.GetAttribute("rel") == "image" ? reader.GetAttribute("href").Replace("_normal.", "_bigger.") : "";
            reader.ReadToFollowing("author");
            //TODO: Use User class instead
            string user = reader.ReadToDescendant("name") ? reader.ReadElementContentAsString() : "";
			string userUri = reader.ReadToFollowing("uri") ? reader.ReadElementContentAsString() : "";

			FeedItem message = new FeedItem(user, text, imageUrl, id) { UserUri = userUri };
            return message;
        }

        private User ParseUser(XmlReader reader)
        {
            int id = reader.ReadToFollowing("id") ? reader.ReadElementContentAsInt():0;
            string name = reader.ReadToFollowing("name") ? reader.ReadElementContentAsString() : "";
            string screenName = reader.ReadToFollowing("screen_name") ? reader.ReadElementContentAsString() : "";
            string location = reader.ReadToFollowing("location") ? reader.ReadElementContentAsString() : "";
            string description = reader.ReadToFollowing("description") ? reader.ReadElementContentAsString() : ""; ;
            string imageUrl = reader.ReadToFollowing("profile_image_url") ? reader.ReadElementContentAsString() : ""; ;
            return new User(this, id, name, screenName, location, description, imageUrl);
        }

        public Client(ICredentials credentials)
        {
            connection = new Connection(credentials);
            //This is needed for compatibility with the twitter service.
            //.Net sends and Expect by default that is not supported by twitter.
            System.Net.ServicePointManager.Expect100Continue = false;
        }

        public Client(string username, string password)
            : this(new NetworkCredential(username, password))
        {
        }

       public List<User> Followers
        {
            get
            {
                List<User> ret = new List<User>();
                string url = "http://www.twitter.com/statuses/followers/{0}.xml";

                WebRequest request = connection.CreateRequestWithUserName(url);
                using (WebResponse response = request.GetResponse())
                {
                    FindResponseErrors(response);
                    using (XmlReader reader = XmlReader.Create(response.GetResponseStream()))
                    {
                        if (reader.ReadToDescendant("user"))
                        {
                            do
                            {
                                User user = ParseUser(reader);
                                ret.Add(user);
                            } while (reader.ReadToFollowing("user"));
                        }
                    }
                }
                return ret;
            }
        }

        public List<User> Friends
        {
            get
            {
                List<User> ret = new List<User>();
                string url = "http://www.twitter.com/statuses/friends/{0}.xml";

                WebRequest request = connection.CreateRequestWithUserName(url);
                using (WebResponse response = request.GetResponse())
                {
                    FindResponseErrors(response);
                    using (XmlReader reader = XmlReader.Create(response.GetResponseStream()))
                    {
                        if (reader.ReadToDescendant("user"))
                        {
                            do
                            {
                                User user = ParseUser(reader);
                                ret.Add(user);
                            } while (reader.ReadToFollowing("user"));
                        }
                    }
                }
                return ret;
            }
        }

        internal void UpdateStatus(string message)
        {
            string uri = "http://www.twitter.com/statuses/update.xml";
            PostToTwitter(uri, string.Concat("status=", Uri.EscapeDataString(message)));
        }

        internal void SendMessage(string name, string message)
        {
            string uri = "http://www.twitter.com/statuses/update.xml";
            PostToTwitter(uri, string.Concat("status=", Uri.EscapeDataString(string.Format("@{0} {1}", name, message)),
                              "&in_reply_to_status_id="));
        }

        internal void SendPrivateMessage(string name, string message)
        {
            string uri = "http://www.twitter.com/direct_messages/new.xml";
            PostToTwitter(uri, string.Concat("user=", name, "&text=", Uri.EscapeDataString(message)));
        }

        private string PostToTwitter(string uri, string message)
        {
            WebRequest request = connection.CreateRequest(uri);
            request.Method = "POST";
            byte[] data = Encoding.UTF8.GetBytes(message);
            request.ContentType = "application/x-www-form-urlencoded";
            HttpWebRequest http = request as HttpWebRequest;
            if (http != null)
            {
                http.UserAgent = "Visual Studio";
            }
            request.ContentLength = data.Length;

            // Submit the data.
            using (Stream requestStream = request.GetRequestStream())
            {
                requestStream.Write(data, 0, data.Length);

                using (WebResponse response = request.GetResponse())
                {
                    FindResponseErrors(response);
                    using (Stream responseStream = response.GetResponseStream())
                    {
                        // throw if an error happened.
                        return new StreamReader(responseStream).ReadToEnd();
                    }
                }
            }
        }
    }
}
